#include "SocketTimeOut.h"



static void connect_alarm(int);
static void recv_alarm(int);



int connect_timeout(int sockfd, const SA* servaddr, socklen_t servaddrlen, int nsec)
{
    SIG_HANDLER sigfunc;
    int confd;
    sigfunc = signal(SIGALRM, connect_alarm);
    if(alarm(nsec) != 0)
    {
        printf("alarm was already set.[%s:%s:%d]\n", __FILE__, __FUNCTION__, __LINE__);
    }

    if((confd = connect(sockfd, servaddr, servaddrlen)) < 0)
    {
        close(sockfd);
        if(errno == EINTR)
        {
            errno = ETIMEDOUT;
        }
    }
    alarm(0);
    signal(SIGALRM, sigfunc);
    return confd;
}

void connect_alarm(int signo)
{
    printf("alarm time out.[%s:%s:%d]\n", __FILE__, __FUNCTION__, __LINE__);
}

void echo_client_alarm(FILE* fp, int sockfd, int nsec)
{
    updatesigaction(recv_alarm);

    int n;
    char sendline[MAXLEN], recvline[MAXLEN];
    while (fgets(sendline, MAXLEN-1, fp) != NULL)
    {
        write(sockfd, sendline, strlen(sendline));
        printf("client line :%s\n", sendline);
        alarm(nsec);
        if ((n = recvfrom(sockfd, recvline, MAXLEN-1, 0, NULL, NULL)) < 0)
        {
            if(errno == EINTR)
            {
                printf("socket timeout.[%s:%s:%d]\n", __FILE__, __FUNCTION__, __LINE__);
            }
            else
            {
                printf("recvfrom error, code :%u\tmsg :%s\n", errno, strerror(errno));
                exit(1);
            }
        }
        else
        {
            printf("recv line :%s\n", recvline);
        }
        memset(sendline, 0, sizeof(sendline));
        memset(recvline, 0, sizeof(recvline));    
        alarm(0);
    }
}

void recv_alarm(int signo)
{
    printf("recv timeout.[%s:%s:%d]\n", __FILE__, __FUNCTION__, __LINE__);
}

int readable_timeo(int fd, int sec)
{
    fd_set rset;
    struct timeval tv;

    FD_ZERO(&rset);
    FD_SET(fd, &rset);
    tv.tv_sec = sec;
    tv.tv_usec = 0;

    return (select(fd+1, &rset, NULL, NULL, &tv));
}


void set_recv_timeo(int* sockfd, int msec)
{
    struct timeval tv;
    tv.tv_sec = msec/1000;
    tv.tv_usec = msec%1000 * 1000;

    setsockopt(*sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}


void updatesigaction(SIG_HANDLER sigfunc)
{
    struct sigaction alarmact;
    bzero(&alarmact, sizeof(alarmact));
    alarmact.sa_handler = sigfunc;
    alarmact.sa_flags |= SA_NOMASK; //不重启慢系统调用
    // alarmact.sa_flags |= SA_RESTART; //自动重启慢系统调用
    sigaction(SIGALRM, &alarmact, NULL);
}